package modelo.Trayectoria;

import modelo.Orientacion.Direccion;
import modelo.Orientacion.Posicion;
import java.util.ArrayList;

public abstract class ListaDeSegmentos {

    private ArrayList<Direccionable> segmentos;
    private MotorDeTrayectoria motor;
    private int velocidad;

    public ListaDeSegmentos(Posicion inicio, Direccion dirInicial, int velocidad){
        SegmentoConDireccion segmentoIni = new SegmentoConDireccion(inicio, dirInicial);
        motor = new MotorDeTrayectoria();
        segmentos = new ArrayList<Direccionable>();
        segmentos.add(segmentoIni);
        this.velocidad = velocidad;
    }

    private Posicion devolverFinDelSegmento(Direccionable segmento){
    /*PreCondicion: la lista debe estar creada
      PostCondicion: devuelve la ultima posicion con la que se puede llegar en esta lista*/
        if (segmento.tienesFin() == false)   return ((SegmentoConDireccion)segmento).devolverPosicionActual();
        return ((SegmentoFijo)segmento).devolverElFin();
    }

    private void unirListas(ArrayList<Direccionable> lista){
    /*PreCondicion: la lista debe estar creada al igual que la lista que se pasa por parametro
      PostCondicion: guarda los direccionables de la lista pasada por parametro a la lista de segmentos*/
        int i;
        Direccionable segmento = segmentos.get(0);
        if (!segmento.tienesFin()) segmentos.remove(0);
        for(i = 0; i < lista.size(); i++){
            segmentos.add(lista.get(i));
        }
    }

    private void cambiarDeSegmento(){
    /*PreCondicion: la lista debe estar creada
      PostCondicion: si hay un segmento mas entonces se borra ese segmento y se sigue con el siguiente,
      en caso contrario crea un segmento sin fin con la direccion y la posicion actual del ultimo segmento*/
        Direccionable segmento;
        SegmentoConDireccion segmentoNuevo;
        Direccion dir;
        Posicion fin;
        if (segmentos.size() >1){
            segmentos.remove(0);
        }else{
            segmento = segmentos.get(0);
            fin = segmento.devolverPosicionActual();
            dir = ((SegmentoFijo)segmento).devolverDireccion();
            segmentoNuevo = this.crearNuevoSegmentoConDireccion(fin, dir);
            segmentos.remove(0);
            segmentos.add(segmentoNuevo);
        }
    }

    protected abstract SegmentoConDireccion crearNuevoSegmentoConDireccion(Posicion pos, Direccion dir);
    /*PreCondicion: la lista debe estar creada
      PostCondicion: devuelve un segmento con direccion segun los parametros enviados*/

    public void agregarSegmento(Posicion fin){
    /*PreCondicion: la lista debe estar creada
      PostCondicion: crea un nuevo segmento desde la ultima posicion del ultimo segmento
      hasta el fin pasado por parametro*/
        Direccionable segmento = segmentos.get(segmentos.size()-1);
        Posicion inicio = this.devolverFinDelSegmento(segmento);
        ArrayList<Direccionable> segmentosNuevos = motor.devolverSegmentos(inicio, fin);
        this.unirListas(segmentosNuevos);
    }

    public void avanzar(){
    /*PreCondicion: la lista debe estar creada
      PostCondicion: si el primer segmento no llego a su fin entonces avanza, en caso contrario
      se cambia de segmento y se avanza*/
        int i;
        Direccionable segmento = segmentos.get(0);
       // segmento.avanzar();
        for(i=0; i<velocidad; i++){
            segmento.avanzar();
            if (!(segmento.avanzo())){
                this.cambiarDeSegmento();
                segmento = segmentos.get(0);
                segmento.avanzar();
            }
        }
    }

    public Posicion devolverPosicionActual(){
    /*PreCondicion: La lista debe estar creada
      PostCondicion: devuelve la posicion actual del primer segmento en la lista*/
        Direccionable segmento = segmentos.get(0);
        return segmento.devolverPosicionActual();
    }

    public void invertirTrayectoria(){
    /*PreCondicion: la lista debe estar creada
      PostCondicion: la trayectoria se invirtio*/
        Direccionable segmento = segmentos.get(0);
        Direccionable segmentoOpuesto = motor.devolverSegmentoOpuesto(segmento);
        segmentos.remove(0);
        segmentos.add(segmentoOpuesto);
    }

    public boolean tienesFin(){
    /*PreCondicion: la lista debe estar creada
      PostCondicion: devuelve true si la trayectoria tiene un fin, en caso contrario devuelve false*/
        Direccionable segmento = segmentos.get(segmentos.size()-1);
        return segmento.tienesFin();
    }

    public void borrarLista(){
    /*PreCondicion: la lista debe estar creada
      PostCondicion: borra la lista de segmentos y la agrega un segmento con la direccion y posicion actual*/
        Direccionable segmento = segmentos.get(0);
        Direccionable segmentoNuevo = this.crearNuevoSegmentoConDireccion(segmento.devolverPosicionActual(), segmento.devolverDireccion());
        segmentos.clear();
        segmentos.add(segmentoNuevo);
    }

    public Direccion devolverLaDireccionActual(){
    /*PreCondicion: la lista debe estar creada
      PostCondicion: devuelve la direccion de la posicion actual*/
        Direccionable segmento = segmentos.get(0);
        return segmento.devolverDireccion();
    }

}
